/**
 * 注册/登录/登出/修改密码/忘记密码 路由
 */

var router = require('express').Router();
var assertAlljs = require('../bin/util/assert-alljs');
var uuid = require('node-uuid');
var dbentityTool = require('../bin/dbentity');
var redisentityTool = require('../bin/redisentity');
var ObjectID = require('mongodb').ObjectID;
var userTool = require('../bin/user');
var imagemeta = require('../bin/imagemeta');

router.post('/signup', async function(req, res) {

    // 因为编辑会更改 用户名/邮箱/手机号码，会影响对应的登录验证密文 password_n, password_e, password_p,
    // 所以强制输入密码，在登录验证正确时，通过该密码重新生成密文

    let assert = assertAlljs(req, res, 'bodyRender:dyn.useredit.html');

    let ses = req.session;

    if (await assert(typeof(req.body.captcha) !== 'string' || req.body.captcha.toLowerCase() !== req.session.captcha.toLowerCase(), '验证码错误!'))
        return;

    // 通过登录验证密码正确
    let password = req.body.password;
    if (await assert(typeof(password)!=='string' || password==='', '此操作需要用户密码!'))
        return;

    // 当为修改用户信息时，先验证密码
    if (typeof(req.body._id) === 'string' && req.body._id!==''){
        let checkMsg = userTool.check(ses.user);
        if (await assert(checkMsg))
            return;

        let {user:sesUser, filter:sesFilter, error:sesError} = userTool.fromBody(ses.user, password);
        if (await assert(sesError))
            return;

        let sesDocs = await dbentityTool.find('user', sesFilter);
        if (await assert(sesDocs instanceof Error, '数据库异常，登录检查异常'))
            return;
        if (await assert(sesDocs.length !== 1, '没有找到用户'))
            return;

        let pass = await userTool.checkSignin(sesUser, password, sesDocs[0]);
        if (await assert(!pass, '密码验证错误，请检查用户名/密码'))
            return;
    }

    // 根据密码重新生成密文
    let {user, filter, error} = userTool.fromBody(req.body, password);
    if (await assert(error))
        return;

    // 查找重复用户
    let docs = await dbentityTool.find('user', filter);
    if (await assert(docs.length > 1 || (docs.length === 1 && docs[0]._id.toString() !== user._id), '保存用户失败，存在重复的用户名/手机/邮箱'))
        return;

    // 生成细节数据
    if (typeof(req.body._id) === 'string' && req.body._id!==''){
        userTool.copyDetails(user, ses.user);
    }else {
        userTool.newDetails(user);
    }

    // 压缩处理用户头像
    if (typeof(user.portrait) === 'string' &&  user.portrait.replace(/\s/g, '') !== ''){
        let { newsrc, filepath, error } = imagemeta.handleUploadImageSrc(user.portrait);
        user.portrait = newsrc;
    }

    // 保存到数据库
    let upUserId = await dbentityTool.upsert('user', user);
    if (await assert(upUserId instanceof Error))
        return;
    if (await assert(typeof(upUserId)!=='string', '保存用户异常'))
        return;

    user._id = upUserId;

    ses.user = user;

    req.behavior.set(upUserId);

    if (typeof(req.body._id) === 'string' && req.body._id!=='') {
        await ses.pushMessage('保存用户成功');
        res.redirect('./userinfo.html');
    } else {
        await ses.pushMessage('注册成功');
        res.redirect('./');
    }
});

router.post('/signin', async function(req, res) {
    let ses = req.session;
    let assert = assertAlljs(req, res);
    if (await assert(req.body.password==null || req.body.password==='', '此操作需要用户密码!'))
        return;

    let password = req.body.password;
    let name = req.body.name;
    if (await assert(name == null || name === '', '此操作需要用户名/邮箱/手机号!'))
        return;

    let filter = {};
    if (userTool.textLikePhoneno(name))
        filter.phoneno = userTool.handlePhoneno(name);
    else if (userTool.textLikeEmail(name))
        filter.email = name;
    else
        filter.name = name;

    let docs = await dbentityTool.find('user', filter);
    if (await assert(docs instanceof Error, '数据库异常，登录检查异常'))
        return;
    if (await assert(docs.length === 0, '没有找到用户'))
        return;
    if (await assert(docs.length > 1, '异常:找到不止一个用户'))
        return;
    if (await assert(docs[0].status === 1, '用户被禁用'))
        return;

    let signedUser = docs[0];
    let pass = await userTool.checkSignin(filter, password, signedUser);
    if (await assert(pass === false, '密码验证错误，请检查用户名/密码'))
        return;

    await ses.signin(signedUser);

    req.behavior.set(signedUser._id);

    res.redirect('back');
});

router.post('/signout', async function(req, res) {
    let ses = req.session;
    ses.signout();
    await ses.pushMessage('您的账户已退出');
    res.redirect('back');
});

// 修改密码 & 忘记密码重设
router.post('/signpassword', async function(req, res) {

    // 修改密码, 包括4个参数: captcha, password, password_new, password_confirm

    let ses = req.session;
    let assert = assertAlljs(req, res);

    if (await assert(req.body.captcha.toLowerCase() !== req.session.captcha.toLowerCase(), '验证码错误!'))
        return;

    let password_new = req.body.password_new;
    if (await assert(typeof(password_new)!=='string' || password_new==='', '新密码为空或不存在!'))
        return;

    let tgtUser = null;
    // 先找到目标用户
    if (req.body.tid == null || req.body.tid === ''){
        let password = req.body.password;
        if (await assert(typeof(password)!=='string' || password==='', '此操作需要用户密码!'))
            return;

        let checkMsg = userTool.check(ses.user);
        if (await assert(checkMsg))
            return;

        let {user:sesUser, filter:sesFilter, error:sesError} = userTool.fromBody(ses.user, password);
        if (await assert(sesError))
            return;

        tgtUser = await dbentityTool.findOne('user', sesFilter);
        if (await assert(tgtUser instanceof Error, '数据库异常，登录检查异常'))
            return;
        if (await assert(tgtUser == null, '没有找到用户'))
            return;
        // 验证原密码
        let pass = await userTool.checkSignin(sesUser, password, tgtUser);
        if (await assert(!pass, '密码验证错误，请检查用户名/密码'))
            return;
    } else {
        let user_id = await redisentityTool.get('resetpwd', req.body.tid);
        if (await assert(user_id instanceof Error || typeof(user_id) !== 'string', '重设密码失败，可能链接已过期'))
            return;

        tgtUser = await dbentityTool.findOne('user', { _id : ObjectID(user_id) });
        if (await assert(tgtUser instanceof Error, '数据库异常，登录检查异常'))
            return;
        if (await assert(tgtUser == null, '没有找到用户'))
            return;
    }

    // 根据密码重新生成密文
    let {user, filter, error} = userTool.fromBody(tgtUser, password_new);
    if (await assert(error))
        return;

    let updaterSetter = {};
    if ('password_n' in user)
        updaterSetter.password_n = user.password_n;
    if ('password_e' in user)
        updaterSetter.password_e = user.password_e;
    if ('password_p' in user)
        updaterSetter.password_p = user.password_p;

    // 保存到数据库
    let result = await dbentityTool.updateOne('user', filter, {'$set' : updaterSetter});
    if (await assert(result instanceof Error))
        return;
    if (await assert(result.result.ok !== 1, '更新数据库异常'))
        return;

    ses.user = user;

    await ses.pushMessage('修改密码成功');
    res.redirect('./');
});

// 密码遗忘(请求找回)
router.post('/signforgetpassword', async function(req, res) {
    let assert = assertAlljs(req, res);

    if (await assert(req.body.host == null || req.body.host === ''))
        return;

    let sitename = res.locals.blocks[res.locals.blockRoot] || '';
    let taskid = uuid.v4().replace(/-/g, '').substr(6,24);
    let url = require('url').resolve(req.headers.referer, 'changePassword.html?tid='+taskid);

    if (req.body.type === 'email'){
        let user = await dbentityTool.findOne('user', {email:req.body.host});

        if (await assert(user == null, '没有找到该邮箱的用户, 请检查邮箱地址'))
            return;

        if (await assert(user instanceof Error, '数据库查询错误'))
            return;

        await redisentityTool.set('resetpwd', taskid, user._id, 15*60*1000);
        let txt = '<p>我们已收到你找回密码的请求, 请前往以下地址重设密码:</p>';
        txt += '<p><a href="'+url+'">'+url+'</a></p><p>(该地址的有效时间为15分钟)</p>';
        require('../bin/driver/sendmail')(req.body.host, sitename + '网站重置密码邮件(勿回)', txt);
        await assert('已将处理邮件发送到你的邮箱');

    } else if (req.body.type === 'phone'){
        let user = await dbentityTool.findOne('user', {phoneno:userTool.handlePhoneno(req.body.host)});

        if (await assert(user == null, '没有找到该手机的用户, 请检查手机号码'))
            return;

        if (await assert(user instanceof Error, '数据库查询错误'))
            return;

        await redisentityTool.set('resetpwd', taskid, user._id, 15*60*1000);
        let txt = '[来自' + sitename +'的消息]我们已收到你找回密码的请求, 请前往以下地址重设密码:\n';
        txt += require('url').resolve(req.headers.referer, 'changePassword.html?tid='+taskid) + '\n(该地址的有效时间为15分钟)';
        console.log(txt);
        await assert('已将处理短信发送到你的手机号码');
    } else if (req.body.type === 'userid'){ // 管理员强制协助找回
        if (await userTool.check(req.session.user, {role:'admin'}))
            return;

        let user = await dbentityTool.findOne('user', {_id:ObjectID(req.body.host)});

        if (await assert(user == null, '没有找到该用户'))
            return;

        if (await assert(user instanceof Error, '数据库查询错误'))
            return;

        await redisentityTool.set('resetpwd', taskid, user._id, 15*60*1000);
        res.end(url);
    } else {
        await assert('找回方式错误');
    }
});

// 设置角色
router.post('/setrole', async function(req, res){
    let user = req.session.user;
    let assert = assertAlljs(req, res);
    let checkMessage = userTool.check(user, {role:'admin'});
    if (await assert(checkMessage))
        return;

    if (await assert(typeof(req.body.user_id) !== 'string' || req.body.user_id === ''))
        return;

    if (await assert(typeof(req.body.role) !== 'string'))
        return;

    let result = await dbentityTool.updateOne('user', {"_id":ObjectID(req.body.user_id)}, {"$set":{'role':req.body.role}});
    if (await assert(result instanceof Error))
        return;

    await req.session.pushMessage('已设置该用户的角色为:'+req.body.role);

    res.redirect('back');
});

module.exports = router;